module Othello where

import Tablero

data Juego = J Color Tablero
data Jugada = M Posicion | Paso
  deriving (Show, Eq)

data Arbol a = Nodo a [Arbol a] deriving Show
type ArbolJugadas = Arbol ([Jugada], Juego)

type Valuacion = Juego -> Double

instance Show Juego where
  show (J turno tablero) = "\n--Juega el " ++ show turno ++ "--\n" ++ show tablero

arbolDeJugadas :: Juego -> ArbolJugadas
arbolDeJugadas j = Nodo ([], j) $ zipWith agmov movs hijos
  where agmov m (Nodo (ms, r) hs) = Nodo (m : ms, r) (map (agmov m) hs)
        movsJuegos = jugadasPosibles j
        movs = map fst movsJuegos
        hijos = map (arbolDeJugadas . snd) movsJuegos

-- Misc
jugadas :: [Jugada]
jugadas = [jugada | jugada <- map M posiciones]


-- Ejercicio 7
pasar :: Juego -> Juego
pasar (J col tab) = J (opuestoParcial col) tab

jugar :: Jugada -> Juego -> Maybe Juego
jugar Paso juego = Just (pasar juego)
jugar (M pos) (J col tab) =
    if contenido pos tab == Nothing && not (null posList) then
        Just (J (opuestoParcial col) (invertirTodas posList newTab))
    else
        Nothing
    where newTab = poner pos col tab
          posList = posicionesAInvertir pos newTab


-- Ejercicio 8
-- Convierte un Maybe Juego en una lista que tiene al juego, o nada si era
-- Nothing
removerNothing :: Maybe Juego -> [Juego]
removerNothing Nothing = []
removerNothing (Just juego) = [juego]

-- Devuleve las jugadas posibles, omitiendo pasar
jugadasPosiblesSinPasar :: Juego -> [(Jugada, Juego)]
jugadasPosiblesSinPasar juego = [(a, b) | a <- jugadas,
                                          b <- removerNothing (jugar a juego)]

-- Jugadas posibles, teniendo en cuenta pasar
jugadasPosibles :: Juego -> [(Jugada, Juego)]
jugadasPosibles juego = if null res then [(Paso, pasar juego)] else res
    where res = jugadasPosiblesSinPasar juego


-- Ejercicio 9
foldArbol :: (a -> [b] -> b) -> Arbol a -> b
foldArbol f (Nodo a xs) = f a (map (foldArbol f) xs)


-- Ejercicio 10
podar' :: Arbol a -> Int -> Arbol a
podar' = foldArbol g
    where g x xs n = case n of
                        0 -> Nodo x []
                        _ -> Nodo x (map (\ p -> p (n-1)) xs)

podar :: Int -> Arbol a -> Arbol a
podar = flip podar'


-- Ejercicio 11
maximo :: [(Double, [Jugada])] -> (Double, [Jugada])
maximo = foldr1 (\ x y -> if fst x >= fst y then x else y)

-- El máximo de los negativos de una lista
maximoNegativo :: [(Double, [Jugada])] -> (Double, [Jugada])
maximoNegativo = maximo . map (\ (x, y) -> (-x, y))

-- Funcion para el foldr, que se basa en el metodo negamax para simplificar la
-- codificacion
valueMinimax :: Valuacion -> ([Jugada], Juego) -> [(Double, [Jugada])] -> (Double, [Jugada])
valueMinimax val (listaJugadas, juego) xs =
    if null xs then (val juego, listaJugadas) else maximoNegativo xs

-- Elegimos en el arbol de jugadas la rama que cumple con el algoritmo minimax
minimax :: Valuacion -> ArbolJugadas -> (Double, [Jugada])
minimax val = foldArbol (valueMinimax val)

-- Tomamos la primer jugada de la lista devuelta por minimax
mejorJugada :: Valuacion -> ArbolJugadas -> Jugada
mejorJugada val arbol = head (snd (minimax val arbol))


-- Ejercicio 12
-- Dice si un jugador esta obligado a pasar
obligadoAPasar :: Juego -> Bool
obligadoAPasar juego = null (jugadasPosiblesSinPasar juego)

-- Dice si ambos jugadores estan obligados a pasar
ambosObligadosAPasar :: Juego -> Bool
ambosObligadosAPasar juego = obligadoAPasar juego && obligadoAPasar (pasar juego)

-- Devuelve el ganador si ya no se puede jugar mas
ganador :: Juego -> Maybe Color
ganador (J col tab) = if ambosObligadosAPasar (J col tab) then
                          colorConMasFichas tab
                      else
                          Nothing


-- Ejercicio 13
valuacionOthello :: Valuacion
valuacionOthello = \ (J col tab) -> case (ganador (J col tab)) of 
                       Just col -> 1.0
                       Nothing -> (2.0 * fromIntegral (cantFichasDeColor col tab) /
                                         fromIntegral (cantFichasTotales tab) - 1.0)
                       _ -> -1.0
